#ifndef GLOBAL_H
#define GLOBAL_H

#define DECLARE_TEXTURE(textured)	                 shared texture textured : textured;
#define ASSIGN_TEXTURE(textured)	                 Texture = <textured>;
#define DECLARE_MINFILTER(default_filter)			 MINFILTER = default_filter;
#define DECLARE_MAGFILTER(default_filter)			 MAGFILTER = default_filter;
#define DECLARE_MIPFILTER(default_filter)			 MIPFILTER = default_filter;
#define cmWorldViewProj WorldViewProj

/////////////////////////////////////////////////////////////////////////////////////////
// Standard Calculations
/////////////////////////////////////////////////////////////////////////////////////////

float4x4	cmWorldViewProj			: WorldViewProj;
float4		cvScreenOffset			: cvScreenOffset;
float4		cvVertexPowerBrightness : cvVertexPowerBrightness;

// The per-color weighting to be used for luminance	calculations in	RGB	order.
static const float3 LUMINANCE_VECTOR = float3(0.2125f, 0.7154f, 0.0721f);

float4 world_position(float4 screen_pos)
{
    float4 p = mul(screen_pos, cmWorldViewProj);
    p.xy += cvScreenOffset.xy * p.w;
    return p;
}

float4 screen_position(float4 screen_pos)
{
    screen_pos.xy += cvScreenOffset.xy;
    return screen_pos;
}

float4 CalcVertexColour(float4 colour)
{
    float4 result = pow(colour, cvVertexPowerBrightness.x) * cvVertexPowerBrightness.y;
    result.w = colour.w;
    return result;
}

float3 ScaleHeadLightIntensity(float3 colour)
{
    float3 result = colour * cvVertexPowerBrightness.z;
    return result;
}

/////////////////////////////////////////////////////////////////////////////////////////
// HDR Colour Space compression
// Convert to a log or psudeo-log colour space to save high dynamic range data
/////////////////////////////////////////////////////////////////////////////////////////

float3 CompressColourSpace(float3 colour)
{
    // original: return colour / (1.0f + colour);
    // 1x ADD, 1x DIV

    // optimized: 1x ADD, 1x RCP, 1x MUL
    // should be faster on SM3 hardware
    return colour * rcp(1.0f + colour);
}

float3 DeCompressColourSpace(float3 compressed)
{
    // original: return compressed / max(1e-5f, 1.0f - compressed);
    // 1x SUB, 1x MAX, 1x DIV

    // optimized: 1x SUB, 1x MAX, 1x RCP, 1x MUL
    // replaces really slow function (div) with 2 faster ones (rcp, mul).
    float denominator = max(1e-5f, 1.0f - compressed);
    return compressed * rcp(denominator);
}

/////////////////////////////////////////////////////////////////////////////////////////
// RGBE8 Encoding/Decoding
// The RGBE8 format stores a mantissa per color channel and a shared exponent 
// stored in alpha. Since the exponent is shared, it's computed based on the
// highest intensity color component. The resulting color is RGB * 2^Alpha,
// which scales the data across a logarithmic scale.
/////////////////////////////////////////////////////////////////////////////////////////

float4 EncodeRGBE8(in float3 rgb)
{
    float4 vEncoded;

    // Determine the largest color component
    // Clamp the maximum component to a small positive value to avoid taking
    // log2(0) when all RGB channels are zero. 
    // Without this guard the exponent would become -Inf and contaminate
    // downstream calculations.
    float maxComponent = max(max(rgb.r, rgb.g), rgb.b);
    maxComponent = max(maxComponent, 1e-6);

    // Round to the nearest integer exponent
    float fExp = ceil(log2(maxComponent));

    // Divide the components by the shared exponent
    vEncoded.rgb = rgb / exp2(fExp);

    // Store the shared exponent in the alpha channel
    vEncoded.a = (fExp + 128) / 255;

    return vEncoded;
}

/////////////////////////////////////////////////////////////////////////////////////////

float3 DecodeRGBE8(in float4 rgbe)
{
    float3 vDecoded;

    // Retrieve the shared exponent
    float fExp = rgbe.a * 255 - 128;

    // Multiply through the color components
    vDecoded = rgbe.rgb * exp2(fExp);

    // Clamp the decoded HDR colour to [0, +inf).  Negative values can arise
    // from denormalised inputs or rounding errors and will cause issues in
    // subsequent operations.
    vDecoded = max(vDecoded, 0);

    return vDecoded;
}

/////////////////////////////////////////////////////////////////////////////////////////
// Colour space conversions (sRGB ↔ linear)
// These helpers convert between sRGB and linear colour spaces using a simple
// power approximation.  Use srgbToLinear when sampling sRGB textures and
// linearToSrgb when writing out colours for presentation.
/////////////////////////////////////////////////////////////////////////////////////////
float3 srgbToLinear(float3 c)
{
    c = max(c, 0);
    // Linearisierungsschwelle: unter 0.04045 ist sRGB annähernd linear
    float3 lo = c / 12.92;
    float3 hi = pow((c + 0.055) / 1.055, 2.4);
    return (c <= 0.04045) ? lo : hi;
}

float3 linearToSrgb(float3 c)
{
    c = max(c, 0);
    float3 lo = c * 12.92;
    float3 hi = 1.055 * pow(c, 1.0 / 2.4) - 0.055;
    return (c <= 0.0031308) ? lo : hi;
}

/////////////////////////////////////////////////////////////////////////////////////////
// Utility pseudo-random generator
// Generates a deterministic pseudo-random value in the range [0,1] from a 2D seed.
// Useful for adding variation to particle rotations or offsets without requiring
// additional textures.  See particles.fx for an example use.
/////////////////////////////////////////////////////////////////////////////////////////
float rand2d(float2 seed)
{
    return frac(sin(dot(seed, float2(12.9898, 78.233))) * 43758.5453);
}

/////////////////////////////////////////////////////////////////////////////////////////
#endif
